home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 September / Macworld (1998-09).dmg / Shareware World / Info / For Developers / MacZoop 1.8.3 / More Classes / File Classes / ZBlockFile.cpp < prev    next >
Text File  |  1997-12-24  |  13KB  |  578 lines

  1. /*************************************************************************************************
  2. *
  3. *
  4. *            ObjectMacZapp        -- a standard Mac OOP application template
  5. *
  6. *
  7. *
  8. *            ZBlockFile.cpp        -- a generic file object that includes some basic block
  9. *                                    management facilities. This type of file is useful for
  10. *                                    implementing VM schemes and databases, etc.
  11. *
  12. *
  13. *            © 1996, Graham Cox
  14. *
  15. *
  16. *
  17. *
  18. *************************************************************************************************/
  19.  
  20.  
  21. #include    "ZBlockFile.h"
  22. #include    "ZArray.h"
  23. #include    "ZErrors.h"
  24. #include    "ZDefines.h"
  25.  
  26.  
  27.  
  28. ZBlockFile::ZBlockFile( const FSSpec& aFileSpec )
  29.     : ZFile( aFileSpec )
  30. {
  31.     bmap = NULL;
  32.     refSeed = 1;
  33.     useCount = 0;
  34. }
  35.  
  36.  
  37. ZBlockFile::ZBlockFile( Str255 fName )
  38.     : ZFile( fName )
  39. {
  40.     bmap = NULL;
  41.     refSeed = 1;
  42.     useCount = 0;
  43. }
  44.  
  45.  
  46. ZBlockFile::~ZBlockFile()
  47. {
  48.     if ( bmap )
  49.         ForgetObject( bmap );
  50. }
  51.  
  52.  
  53. /*------------------------------------***  OPEN  ***------------------------------------*/
  54. /*    
  55. open the file and initialise the map, from file if this is overridden to do so.
  56. ----------------------------------------------------------------------------------------*/
  57.  
  58. void            ZBlockFile::Open()
  59. {
  60.     ZFile::Open();
  61.     
  62.     InitMap();
  63.     ReadMap();    
  64. }
  65.  
  66.  
  67.  
  68. /*-------------------------------***  GETBLOCKCOUNT  ***--------------------------------*/
  69. /*    
  70. return the total number of blocks
  71. ----------------------------------------------------------------------------------------*/
  72.  
  73. long            ZBlockFile::GetBlockCount()
  74. {
  75.     if ( bmap )
  76.         return bmap->CountItems();
  77.     else
  78.         return 0;
  79. }
  80.  
  81.  
  82. /*--------------------------------***  GETBLOCKREF  ***---------------------------------*/
  83. /*    
  84. get the ref for the indexed block
  85. ----------------------------------------------------------------------------------------*/
  86.  
  87. long            ZBlockFile::GetBlockRef( const long bkIndex )
  88. {
  89.     Block    b;
  90.     
  91.     if ( bmap )
  92.     {
  93.         bmap->GetArrayItem( &b, bkIndex );
  94.         
  95.         return b.fRefNum;
  96.     }
  97.     else
  98.         return -1;    
  99. }
  100.  
  101.  
  102. /*------------------------------------***  CLOSE  ***-----------------------------------*/
  103. /*    
  104. write header and map, then close the file as normal
  105. ----------------------------------------------------------------------------------------*/
  106.  
  107. void            ZBlockFile::Close()
  108. {
  109.     WriteHeader();
  110.     WriteMap();
  111.     
  112.     ZFile::Close();
  113.     
  114.     ForgetObject( bmap );
  115.     bmap = NULL;    
  116. }
  117.  
  118.  
  119. /*----------------------------------***  ADDBLOCK  ***----------------------------------*/
  120. /*    
  121. register a new block in the file's map. This allocates a ref num and returns it.
  122. ----------------------------------------------------------------------------------------*/
  123.  
  124. long            ZBlockFile::AddBlock()
  125. {
  126.     long    aRef = GetNewRefSeed();
  127.     
  128.     AddBlockUsingRef( aRef );
  129.     
  130.     return aRef;
  131. }
  132.  
  133.  
  134. /*------------------------------***  ADDBLOCKUSINGREF  ***------------------------------*/
  135. /*    
  136. register a new block using the ref passed. Caller MUST ensure this is unique.
  137. ----------------------------------------------------------------------------------------*/
  138.  
  139. void            ZBlockFile::AddBlockUsingRef( const long aRef )
  140. {
  141.     if ( GetBlock( aRef ) == 0 )
  142.     {
  143.         useCount++;
  144.         
  145.         // initially we set the size to zero, since we don't know until the data is written
  146.         // how big it is. This is fine, since a) we don't want to expand the file until we
  147.         // have to, and b) SetBlockData automatically manages the reallocation of blocks as
  148.         // needed to satisfy the size request.
  149.         
  150.         AppendBlock( aRef, 0 );
  151.     }
  152.     else
  153.         FailOSErr( kBlockDupRefErr );
  154. }
  155.  
  156.  
  157. /*--------------------------------***  REMOVEBLOCK  ***---------------------------------*/
  158. /*    
  159. unregister block from file, returning the space it occupies as a free block.
  160. ----------------------------------------------------------------------------------------*/
  161.  
  162. void            ZBlockFile::RemoveBlock( const long ref )
  163. {
  164.     long    i;
  165.     
  166.     if ( i = GetBlock( ref ) > 0 )
  167.     {
  168.         useCount--;
  169.         
  170.         FreeBlock( i );
  171.         CompactMap();
  172.     }
  173.     else
  174.         FailOSErr( kBlockNotFoundErr );
  175. }
  176.  
  177.  
  178. /*--------------------------------***  GETBLOCKDATA  ***--------------------------------*/
  179. /*    
  180. get the data of a block into an arbitrary buffer.
  181. ----------------------------------------------------------------------------------------*/
  182.  
  183. void            ZBlockFile::GetBlockData( const long ref, void* dataPtr, long* dataLen )
  184. {
  185.     long    i = GetBlock( ref );
  186.     long    iSize;
  187.     
  188.     if ( i > 0 )
  189.     {
  190.         iSize = GetBlockSize( i );
  191.         
  192.         SetMarkToBlock( i );
  193.         
  194.         iSize = MIN( iSize, *dataLen );
  195.         Read((Ptr) dataPtr, &iSize );
  196.         
  197.         *dataLen = iSize;
  198.     
  199.     }
  200.     else
  201.         FailOSErr( kBlockNotFoundErr );
  202. }
  203.  
  204.  
  205. /*--------------------------------***  GETBLOCKDATA  ***--------------------------------*/
  206. /*    
  207. get the data of a blovk into an existing handle, replacing its contents and resizing as
  208. necessary
  209. ----------------------------------------------------------------------------------------*/
  210.  
  211. void            ZBlockFile::GetBlockData( const long ref, Handle dataH )
  212. {
  213.     char    hs;
  214.     long    iSize, i;
  215.     
  216.     FailNILParam( dataH );
  217.     i = GetBlock( ref );
  218.     
  219.     if ( i > 0 )
  220.     {
  221.         hs = HGetState( dataH );
  222.     
  223.         iSize = GetBlockSize( i );
  224.         
  225.         SetHandleSize( dataH, iSize );
  226.         FailOSErr( MemError());
  227.         
  228.         SetMarkToBlock( i );
  229.         
  230.         HLock( dataH );
  231.         Read( *dataH, &iSize );
  232.         
  233.         HSetState( dataH, hs );
  234.     }
  235.     else
  236.         FailOSErr(     kBlockNotFoundErr );    
  237. }
  238.  
  239.  
  240. /*--------------------------------***  SETBLOCKDATA  ***--------------------------------*/
  241. /*    
  242. put data from an arbitrary buffer into the file, associated with the block ref. This
  243. splits or reallocates blocks as needed.
  244. ----------------------------------------------------------------------------------------*/
  245.  
  246. void            ZBlockFile::SetBlockData( const long ref, void* dataPtr, long dataLen )
  247. {
  248.     long    iSize, i;
  249.     
  250.     i = GetBlock( ref );
  251.     
  252.     if ( i > 0 )
  253.     {
  254.         iSize = GetBlockSize( i );
  255.         
  256.         // if the size of the block doesn't match <dataLen>, we may need to reallocate
  257.         // or split the block
  258.         
  259.         if ( iSize != dataLen )
  260.         {
  261.             ReallocBlock( ref, dataLen );
  262.             
  263.             i = GetBlock( ref );
  264.             
  265.             if ( i <= 0 )
  266.                 FailOSErr( kBlockBadRefErr );
  267.                 
  268.             iSize = GetBlockSize( i );
  269.             
  270.             CompactMap();
  271.         }
  272.         
  273.         // save the data to the block
  274.         
  275.         SetMarkToBlock( i );
  276.         Write((Ptr) dataPtr, &iSize );
  277.     }
  278.     else
  279.         FailOSErr( kBlockNotFoundErr );
  280.     
  281. }
  282.  
  283.  
  284. /*--------------------------------***  SETBLOCKDATA  ***--------------------------------*/
  285. /*    
  286. put data from a handle into the file associated with the block ref.
  287. ----------------------------------------------------------------------------------------*/
  288.  
  289. void            ZBlockFile::SetBlockData( const long ref, Handle dataH )
  290. {
  291.     char hs;
  292.     
  293.     hs = HGetState( dataH );
  294.     HLock( dataH );
  295.     
  296.     try
  297.     {
  298.         SetBlockData( ref, *dataH, GetHandleSize( dataH ));
  299.     }
  300.     catch( OSErr err )
  301.     {
  302.         HSetState( dataH, hs );
  303.         
  304.         throw err;
  305.     }
  306.     
  307.     HSetState( dataH, hs );
  308. }
  309.  
  310.  
  311. /*--------------------------------***  APPENDBLOCK  ***---------------------------------*/
  312. /*    
  313. assign a new block to contain the bytes requested. This initially tries to find a free
  314. block big enough, splitting it if necessary, otherwise it appends a block to the end of
  315. the file, growing it.
  316. ----------------------------------------------------------------------------------------*/
  317.  
  318. void            ZBlockFile::AppendBlock( const long ref, const unsigned long sizeNeeded )
  319. {
  320.     Block     b;
  321.     long    i;
  322.     
  323.     i = FindFreeBlock( sizeNeeded );
  324.     
  325.     if ( i > 0 )
  326.     {
  327.         // we found a block big enough to contain the data, so let's see what's in it:
  328.         
  329.         bmap->GetArrayItem( &b, i );
  330.         
  331.         // need to split it?
  332.         
  333.         if ( sizeNeeded < b.fSize )
  334.             SplitBlock( i, sizeNeeded );
  335.             
  336.         // set up entry- the mark & length fields are already set, so don't touch it:
  337.         
  338.         b.fRefNum = ref;
  339.         b.fStatus = notFree;
  340.         
  341.         bmap->SetArrayItem( &b, i );
  342.     }
  343.     else
  344.     {
  345.         // no block was found that can contain the size requested, so we'll have to append
  346.         // a new block to the end of the file.
  347.         
  348.         b.fRefNum = ref;
  349.         b.fStatus = notFree;
  350.         b.fMark = GetLength();
  351.         b.fSize = sizeNeeded;
  352.         
  353.         // grow the file to the desired length
  354.         
  355.         SetLength( b.fMark + sizeNeeded );
  356.         
  357.         // add item to map
  358.         
  359.         bmap->AppendItem( &b );
  360.     }
  361. }
  362.  
  363.  
  364. /*-------------------------------***  FINDFREEBLOCK  ***--------------------------------*/
  365. /*    
  366. returns the index of a free block large enough to contain the bytes passed, or 0 if none
  367. could be found.
  368. ----------------------------------------------------------------------------------------*/
  369.  
  370. long            ZBlockFile::FindFreeBlock( const unsigned long sizeNeeded )
  371. {
  372.     // locate a free block that can contain <sizeNeeded>.
  373.     
  374.     long        i;
  375.     Block        b;
  376.     Boolean        found = FALSE;
  377.     
  378.     for ( i = 1; i <= bmap->CountItems(); i++ )
  379.     {
  380.         bmap->GetArrayItem( &b, i );
  381.         
  382.         if (( b.fSize <= sizeNeeded ) && ( b.fStatus == free ))
  383.         {
  384.             found = TRUE;
  385.             break;
  386.         }
  387.     }
  388.     
  389.     if ( found )
  390.         return i;
  391.     else
  392.         return 0;
  393. }
  394.  
  395.  
  396. /*----------------------------------***  GETBLOCK  ***----------------------------------*/
  397. /*    
  398. return the map index of a block with the given ref. This is a simple search through the
  399. map.
  400. ----------------------------------------------------------------------------------------*/
  401.  
  402. long            ZBlockFile::GetBlock( const long ref )
  403. {
  404.     // locate the block with the given ref
  405.     
  406.     long        i;
  407.     Block        b;
  408.     Boolean        found = FALSE;
  409.     
  410.     for ( i = 1; i <= bmap->CountItems(); i++ )
  411.     {
  412.         bmap->GetArrayItem( &b, i );
  413.         
  414.         if ( b.fRefNum == ref )
  415.         {
  416.             found = TRUE;
  417.             break;
  418.         }
  419.     }
  420.     
  421.     if ( found )
  422.         return i;
  423.     else
  424.         return 0;
  425. }
  426.  
  427.  
  428. /*---------------------------------***  FREEBLOCK  ***----------------------------------*/
  429. /*    
  430. mark the block with the index passed as available for re-use.
  431. ----------------------------------------------------------------------------------------*/
  432.  
  433. void            ZBlockFile::FreeBlock( const long bkIndex )
  434. {
  435.     // mark the block with the index passed as free.
  436.     
  437.     Block    b;
  438.     
  439.     bmap->GetArrayItem( &b, bkIndex );
  440.     
  441.     b.fRefNum = 0;
  442.     b.fStatus = free;
  443.     
  444.     // if the block size is zero, it may as well be deleted from the map, since
  445.     // it is only cluttering it up and is not managing any space in the file.
  446.     
  447.     if ( b.fSize == 0 )
  448.         bmap->DeleteItem( bkIndex );
  449.     else
  450.         bmap->SetArrayItem( &b, bkIndex );
  451. }
  452.  
  453.  
  454. /*---------------------------------***  SPLITBLOCK  ***---------------------------------*/
  455. /*    
  456. divide a block into a free part and a used part. This is done whenever a block becomes
  457. smaller.
  458. ----------------------------------------------------------------------------------------*/
  459.  
  460. void            ZBlockFile::SplitBlock( const long bkIndex, const unsigned long bkSize )
  461. {
  462.     // splits a block in two, returning unused space to the pool.
  463.     
  464.     Block    b, subBlock;
  465.     
  466.     bmap->GetArrayItem( &b, bkIndex );
  467.     
  468.     // the sub-block will manage the bytes left over once <bkSize> has been
  469.     // alloted to this block.
  470.     
  471.     subBlock.fRefNum = 0;
  472.     subBlock.fMark = b.fMark + bkSize;
  473.     subBlock.fSize = b.fSize - bkSize;
  474.     subBlock.fStatus = free;
  475.     
  476.     // sanity check- subBlock can't be 0 or negative:
  477.     
  478.     FailOSErr(( subBlock.fSize <= 0 )? kBadSplitReqErr : noErr );
  479.     
  480.     // now change block to exact size
  481.     
  482.     b.fSize = bkSize;
  483.     
  484.     // add and update map items
  485.     
  486.     bmap->AppendItem( &subBlock );
  487.     bmap->SetArrayItem( &b, bkIndex );
  488. }
  489.  
  490.  
  491. /*--------------------------------***  REALLOCBLOCK  ***--------------------------------*/
  492. /*    
  493. reassign the block <ref> to a new physical place in the file. This is done whenever the
  494. data size changes, and it attempts to reuse space where possible.
  495. ----------------------------------------------------------------------------------------*/
  496.  
  497. void            ZBlockFile::ReallocBlock( const long ref, long newBkSize )
  498. {
  499.     // reassign the data to another block, since it's size has changed.
  500.     
  501.     long    i;
  502.     Block    b;
  503.     
  504.     i = GetBlock( ref );
  505.     
  506.     if ( i > 0 )
  507.     {
  508.         bmap->GetArrayItem( &b, i );
  509.     
  510.         // if new size is LESS than block, we can simply split it, returning the excess to
  511.         // the free space.
  512.         
  513.         if ( newBkSize < b.fSize )
  514.             SplitBlock( i, newBkSize );
  515.         else
  516.         {
  517.             // new data is LARGER than before, so we free the whole block and allocate a
  518.             // new one.
  519.             
  520.             FreeBlock( i );
  521.             AppendBlock( ref, newBkSize );
  522.         }
  523.     }
  524.     else
  525.         FailOSErr( kBlockNotFoundErr );
  526. }
  527.  
  528.  
  529. /*--------------------------------***  GETBLOCKSIZE  ***--------------------------------*/
  530. /*    
  531. return the size of the indexed block
  532. ----------------------------------------------------------------------------------------*/
  533.  
  534. unsigned long    ZBlockFile::GetBlockSize( const long bkIndex )
  535. {
  536.     Block    b;
  537.     
  538.     bmap->GetArrayItem( &b, bkIndex );
  539.     return b.fSize;
  540. }
  541.  
  542.  
  543. /*-------------------------------***  SETMARKTOBLOCK  ***-------------------------------*/
  544. /*    
  545. position the filemark at the place alloted by the indexed block.
  546. ----------------------------------------------------------------------------------------*/
  547.  
  548. void            ZBlockFile::SetMarkToBlock( const long bkIndex )
  549. {
  550.     Block    b;
  551.     
  552.     bmap->GetArrayItem( &b, bkIndex );
  553.     SetMark( b.fMark );    
  554. }
  555.  
  556.  
  557. /*-----------------------------------***  INITMAP  ***----------------------------------*/
  558. /*    
  559. initialise the block map (creates it anew)
  560. ----------------------------------------------------------------------------------------*/
  561.  
  562. void            ZBlockFile::InitMap()
  563. {
  564.     FailNIL( bmap = new ZArray( sizeof( Block )));
  565. }
  566.  
  567.  
  568. /*--------------------------------***  COMPACTMAP  ***----------------------------------*/
  569. /*    
  570. conjoin adjacent free blocks into larger free blocks, thus increasing their chances of
  571. being reused. This is a moderately efficient operation in that it does not change anything
  572. in the file itself, but simply modifies the map.
  573. ----------------------------------------------------------------------------------------*/
  574.  
  575. void            ZBlockFile::CompactMap()
  576. {
  577.     // TO DO
  578. }